\subsubsection{ARM64}

\myparagraph{GCC}

Skompilujmy przykład w GCC 4.8.1 dla ARM64:

\lstinputlisting[numbers=left,label=hw_ARM64_GCC,caption=\NonOptimizing GCC 4.8.1 + objdump,style=customasmARM]{patterns/01_helloworld/ARM/hw.lst}

W ARM64 nie ma trybów Thumb i Thumb-2, tylko ARM, dlatego tu korzystamy tylko z 32-bitowych instrukcji.

Jest tu dwa razy więcej rejestrów: \myref{ARM64_GPRs}.
64-bitowe rejestry mają prefiks 
\TT{X-}, a ich 32-bitowe części --- \TT{W-}.

\myindex{ARM!\Instructions!STP}
Instrukcja \TT{STP} (\IT{Store Pair}) 
odkłada na stos od razu 2 rejestry: \RegX{29} i \RegX{30}.
Oczywiście, ta instrukcja może zapisać tę parę rejestrów gdziekolwiek w pamięci, ale tu jest wskazany rejestr \ac{SP}, także ta para jest odkładana na stos.

Rejestry w ARM64 są 64-bitowe, każdy o długości 8 bajtów, dlatego do przechowywania 2 rejestrów potrzeba 16 bajtów.

Wykrzyknik (``!'') po operandzie oznacza, że najpierw od \ac{SP} będzie odjęte 16 i dopiero po tej czynności wartości z obu rejestrów będą odłożone na stos.

Jest to nazywane \IT{pre-index}.
Więcej o róznicy między \IT{post-index} a \IT{pre-index} 
można znaleźć tu: \myref{ARM_postindex_vs_preindex}.

W ten sposób, posługując się terminologią x86, pierwsza instrukcja~--- jest analogiczna do \TT{PUSH X29} i \TT{PUSH X30}.
\RegX{29} w ARM64 jest wykorzystywane jako \ac{FP}, a \RegX{30} 
jako \ac{LR}, dlatego są one zapisywane w prologu funkcji.

Druga instrukcja kopiuje \ac{SP} do \RegX{29} (lub \ac{FP}).
Jest to niezbędne do ustawienia stack frame funkcji.

\label{pointers_ADRP_and_ADD}
\myindex{ARM!\Instructions!ADRP/ADD pair}
Instrukcje \TT{ADRP} i \ADD są potrzebne do skonstruowania adresu linii \q{Hello!} w rejestrze \RegX{0}, 
jako że pierwszy argument f-cji jest przekazywany przez ten rejestr.
Jednakże w ARM nie ma instrukcji, za pomocą których można zapisać do rejestru dużą liczbę 
(dlatego że długość instrukcji wynosi maksymalnie 4 bajty. Więcej informacji o tym można znaleźć tutaj: \myref{ARM_big_constants_loading}).
Dlatego trzeba skorzystać z kilku instrukcji.
Pierwsza instrukcja (\TT{ADRP}) zapisuje do \RegX{0} adres 4-kB strony, na której się znajduje linia, 
a druga (\ADD) dodaje do tego adresu resztę.
Więcej o tym tutaj: \myref{ARM64_relocs}.

\TT{0x400000 + 0x648 = 0x400648}, i możemy zobaczyć, że w segmencie danych \TT{.rodata} pod tym adresem znajduje się nasza
linia \q{Hello!}.

\myindex{ARM!\Instructions!BL}
Następnie, za pomocą instrukcji \TT{BL} jest wywoływane \puts. Zostało to omówione wcześniej: \myref{puts}.

Instrukcja \MOV zapisuje 0 do \RegW{0}. 
\RegW{0} to młodsze 32 bity 64-bitowego rejestru \RegX{0}:

\input{ARM_X0_register}

Wynik funkcji jest zwracany przez \RegX{0}, i \main zwraca 0.

Dlaczego 32-bitowa część?
Dlatego że w ARM64, jak i w x86-64, typ \Tint zostawili 32-bitowym, dla kompatybilności.

Odpowiednio, jako że funkcja zwraca 32-bitowy \Tint, to trzeba wypełnić tylko młodsze 32 bity 64-bitowego rejestru \RegX{0}.

Żeby mieć pewność, trochę zmienimy przykład i skompilujemy go ponownie.%

Teraz \main zwraca 64-bitową wartość:

\begin{lstlisting}[caption=\main zwracające wartość typu \TT{uint64\_t},style=customc]
#include <stdio.h>
#include <stdint.h>

uint64_t main()
{
        printf ("Hello!\n");
        return 0;
}
\end{lstlisting}

Wynik jest taki sam, tylko \MOV w tej linii wygląda teraz w ten sposób:

\begin{lstlisting}[caption=\NonOptimizing GCC 4.8.1 + objdump]
  4005a4:       d2800000        mov     x0, #0x0      // #0
\end{lstlisting}

\myindex{ARM!\Instructions!LDP}
Następnie za pomocą instrukcji \INS{LDP} (\IT{Load Pair}) są przywracane rejestry \RegX{29} i \RegX{30}.

Wykrzyknika po instrukcji brak. To oznacza, że najpierw wartości są zdejmowane ze stosu, i dopiero po tej czynności \ac{SP} jest zwiększane o 16.

Jest to nazywane \IT{post-index}.

\myindex{ARM!\Instructions!RET}
W ARM64 pojawia się nowa instrukcja: \RET. 
Ona działa tak samo jak i \INS{BX LR}, ale zawiera bit ,
który podpowiada procesorowi, że jest to wyjście z f-cji, a nie zwykłe przejście, żeby procesor mógł zoptymalizować tę instrukcję.

Jako że ta funkcja jest bardzo prosta, optymalizujący GCC generuje dokładnie taki sam kod.


